package aceim.protocol.snuk182.xmpp.common; import java.io.File; import java.io.IOException; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.concurrent.Executors; import org.jivesoftware.smack.ConnectionConfiguration; import org.jivesoftware.smack.ConnectionListener; import org.jivesoftware.smack.Roster; import org.jivesoftware.smack.Roster.SubscriptionMode; import org.jivesoftware.smack.RosterEntry; import org.jivesoftware.smack.SASLAuthentication; import org.jivesoftware.smack.SmackConfiguration; import org.jivesoftware.smack.XMPPConnection; import org.jivesoftware.smack.XMPPException; import org.jivesoftware.smack.packet.IQ.Type; import org.jivesoftware.smack.packet.Presence; import org.jivesoftware.smack.packet.Privacy; import org.jivesoftware.smack.packet.PrivacyItem; import org.jivesoftware.smack.provider.ProviderManager; import org.jivesoftware.smack.proxy.ProxyInfo; import org.jivesoftware.smack.proxy.ProxyInfo.ProxyType; import org.jivesoftware.smackx.MessageEventManager; import org.jivesoftware.smackx.ServiceDiscoveryManager; import org.jivesoftware.smackx.entitycaps.EntityCapsManager; import org.jivesoftware.smackx.filetransfer.FileTransferManager; import org.jivesoftware.smackx.packet.DiscoverItems; import org.jivesoftware.smackx.packet.DiscoverItems.Item; import aceim.api.dataentity.Buddy; import aceim.api.dataentity.BuddyGroup; import aceim.api.dataentity.ConnectionState; import aceim.api.dataentity.FileInfo; import aceim.api.dataentity.FileMessage; import aceim.api.dataentity.ItemAction; import aceim.api.dataentity.MultiChatRoom; import aceim.api.dataentity.OnlineInfo; import aceim.api.dataentity.TextMessage; import aceim.api.service.ApiConstants; import aceim.api.service.ProtocolException; import aceim.api.service.ProtocolException.Cause; import aceim.api.utils.Logger; import aceim.api.utils.Logger.LoggerLevel; import aceim.protocol.snuk182.xmpp.common.utils.ResourceUtils; import android.text.TextUtils; public class XMPPServiceInternal implements ConnectionListener { public XMPPServiceInternal(XMPPCommonService service) { this.service = service; this.mMyOnlineInfo = new OnlineInfo(service.getServiceId(), service.getProtocolUid()); } private final XMPPCommonService service; private final OnlineInfo mMyOnlineInfo; private XMPPConnection connection; private final XMPPRosterListener mRosterListener = new XMPPRosterListener(this); private final XMPPChatListener mChatListener = new XMPPChatListener(this); private final XMPPFileTransferListener mFileTransferListener = new XMPPFileTransferListener(this); private boolean secureConnection = true; private ServiceDiscoveryManager mServiceDiscoveryManager; public void onXmppException(XMPPException e) { Logger.log(e); getService().getCoreService().notification( ResourceUtils.xmppExceptionToString(e)); } public void connect(OnlineInfo info) throws ProtocolException { final String jid = getService().getCoreService().requestPreference( ResourceUtils.KEY_JID); final String password = getService().getCoreService().requestPreference( ResourceUtils.KEY_PASSWORD); final String host = getService().getCoreService().requestPreference( ResourceUtils.KEY_SERVER_HOST); final String port = getService().getCoreService().requestPreference( ResourceUtils.KEY_SERVER_PORT); String proxyType = getService().getCoreService().requestPreference( ResourceUtils.KEY_PROXY_TYPE); String proxyHost = getService().getCoreService().requestPreference( ResourceUtils.KEY_PROXY_HOST); String proxyPort = getService().getCoreService().requestPreference( ResourceUtils.KEY_PROXY_PORT); String proxyUsername = getService().getCoreService().requestPreference( ResourceUtils.KEY_PROXY_USERNAME); String proxyPassword = getService().getCoreService().requestPreference( ResourceUtils.KEY_PASSWORD); String isSecure = getService().getCoreService().requestPreference( ResourceUtils.KEY_SECURE_CONNECTION); if (jid == null || password == null){ throw new ProtocolException(Cause.BROKEN_AUTH_DATA); } final ProxyInfo proxyInfo; if (proxyHost != null && proxyPort != null && proxyHost.length() > 0 && proxyPort.length() > 0 && proxyType != null && !proxyType.equalsIgnoreCase("none")) { proxyInfo = new ProxyInfo(getProxyType(proxyType), proxyHost, Integer.parseInt(proxyPort.trim().replaceAll("\n", "")), proxyUsername, proxyPassword); } else { proxyInfo = null; } //this.onlineInfo.getFeatures() if (isSecure != null) { try { secureConnection = Boolean.parseBoolean(isSecure); } catch (Exception e) { Logger.log(e); } } if (info != null) { } Runnable r = new Runnable() { @Override public void run() { String un; String serviceName; if (jid.contains("@")) { String[] jidParams = jid.split("@"); un = jidParams[0]; serviceName = jidParams[1]; } else { un = jid; serviceName = host; } SmackConfiguration.setPacketReplyTimeout(120000); ConnectionConfiguration config; if (proxyInfo != null) { config = new ConnectionConfiguration(host, Integer.parseInt(port), proxyInfo); } else { config = new ConnectionConfiguration(host, Integer.parseInt(port)); } String login; if (isGmail(serviceName) && secureConnection) { SASLAuthentication.supportSASLMechanism("PLAIN", 0); login = mMyOnlineInfo.getProtocolUid(); } else { login = un; } config.setSASLAuthenticationEnabled(secureConnection); config.setServiceName(serviceName); try { getService().configure(ProviderManager.getInstance()); connection = new XMPPConnection(config); mRosterListener.setContactListReady(false); getService().getCoreService().connectionStateChanged( ConnectionState.CONNECTING, 1); connection.connect(); mServiceDiscoveryManager = new ServiceDiscoveryManager(connection); ServiceDiscoveryManager.setIdentityName(getService().getContext().getString(R.string.app_name)); EntityCapsManager capsManager = EntityCapsManager.getInstanceFor(connection); capsManager.enableEntityCaps(); Roster roster = connection.getRoster(); roster.setSubscriptionMode(SubscriptionMode.manual); //Attempting to fix buddy presence changes missing Thread.sleep(1000); roster.addRosterListener(mRosterListener); getService().getCoreService().connectionStateChanged( ConnectionState.CONNECTING, 2); connection.login(login, password, getService().getContext().getString(R.string.app_name)); connection.addPacketListener(mRosterListener, mRosterListener); connection.addConnectionListener(XMPPServiceInternal.this); getService().getCoreService().connectionStateChanged( ConnectionState.CONNECTING, 3); setStatus(mMyOnlineInfo.getFeatures().getByte(ApiConstants.FEATURE_STATUS, (byte) 0)); getService().getCoreService().connectionStateChanged( ConnectionState.CONNECTING, 4); while (!isRosterInitialized(roster)) { Logger.log("Roster not ready", LoggerLevel.VERBOSE); Thread.sleep(1000); } getService().getCoreService().connectionStateChanged( ConnectionState.CONNECTING, 6); List<BuddyGroup> groups = mRosterListener.getContactList(); connection.getChatManager().addChatListener(mChatListener); getService().getCoreService().connectionStateChanged( ConnectionState.CONNECTING, 7); try { List<MultiChatRoom> joinedChats = mChatListener.getJoinedChatRooms(); getService().getEntityAdapter().addGroupChats(groups, joinedChats, jid, getService().getServiceId()); } catch (Exception e) { Logger.log(e); } getService().getCoreService().connectionStateChanged( ConnectionState.CONNECTING, 8); getService().getCoreService().buddyListUpdated( groups); getService().getCoreService().connectionStateChanged( ConnectionState.CONNECTING, 9); fillFeatures(); getService().getCoreService().accountStateChanged(mMyOnlineInfo); getService().getCoreService().connectionStateChanged( ConnectionState.CONNECTED, 0); mRosterListener.getBuddyInfo(jid, true, false); mChatListener.setMessageEventManager(new MessageEventManager(connection)); FileTransferManager ftm = new FileTransferManager(connection); ftm.addFileTransferListener(mFileTransferListener); mFileTransferListener.setFileTransferManager(ftm); getService().sendKeepalive(); mRosterListener.setContactListReady(true); mRosterListener.checkCachedInfos(); } catch (Exception e) { Logger.log(e); connection = null; connectionClosedOnError(e); } } }; Executors.defaultThreadFactory().newThread(r).start(); } private void fillFeatures() { if (!mMyOnlineInfo.getFeatures().containsKey(ApiConstants.FEATURE_STATUS)) { mMyOnlineInfo.getFeatures().putByte(ApiConstants.FEATURE_STATUS, (byte) 0); } mMyOnlineInfo.getFeatures().putBoolean(ApiConstants.FEATURE_BUDDY_MANAGEMENT, true); mMyOnlineInfo.getFeatures().putBoolean(ApiConstants.FEATURE_GROUP_MANAGEMENT, true); mMyOnlineInfo.getFeatures().putBoolean(ApiConstants.FEATURE_ACCOUNT_MANAGEMENT, true); mMyOnlineInfo.getFeatures().putBoolean(XMPPApiConstants.FEATURE_ADD_BUDDY, true); try { if (mChatListener.hasGroupchatSupport()) { mMyOnlineInfo.getFeatures().putBoolean(XMPPApiConstants.FEATURE_ADD_GROUPCHAT, true); mMyOnlineInfo.getFeatures().putBoolean(XMPPApiConstants.FEATURE_GROUPCHATS, true); } else { mMyOnlineInfo.getFeatures().remove(XMPPApiConstants.FEATURE_GROUPCHATS); mMyOnlineInfo.getFeatures().remove(XMPPApiConstants.FEATURE_ADD_GROUPCHAT); } } catch (XMPPException e) { mMyOnlineInfo.getFeatures().remove(XMPPApiConstants.FEATURE_GROUPCHATS); mMyOnlineInfo.getFeatures().remove(XMPPApiConstants.FEATURE_ADD_GROUPCHAT); } } @Override public void connectionClosedOnError(Exception e) { Logger.log("Connection closed " + getService().getProtocolUid()); Logger.log(e); getService().closeKeepaliveThread(); mRosterListener.setContactListReady(false); getService().getCoreService().connectionStateChanged( ConnectionState.DISCONNECTED, -1); if ((e instanceof IOException) || (e instanceof XMPPException && ((XMPPException) e).getXMPPError() != null && !((XMPPException) e).getXMPPError().getCondition().equals("remote-server-timeout"))) { getService().getCoreService().notification(e.getLocalizedMessage()); } } private boolean isRosterInitialized(Roster roster) { try { Field field = Roster.class.getDeclaredField("rosterInitialized"); field.setAccessible(true); return (Boolean) field.get(roster); } catch (Exception e) { Logger.log(e); return true; } } private boolean isGmail(String serviceName) { return serviceName.equals("gmail.com") || serviceName.equals("googlemail.com"); } private ProxyType getProxyType(String proxyType) { if (proxyType.equalsIgnoreCase("http")) { return ProxyType.HTTP; } else if (proxyType.equalsIgnoreCase("socks4")) { return ProxyType.SOCKS4; } else if (proxyType.equalsIgnoreCase("socks5")) { return ProxyType.SOCKS5; } return ProxyType.NONE; } @Override public void connectionClosed() { Logger.log("Connection closed " + getService().getProtocolUid()); getService().closeKeepaliveThread(); mRosterListener.setContactListReady(false); getService().getCoreService().connectionStateChanged(ConnectionState.DISCONNECTED, -1); } @Override public void reconnectingIn(int seconds) { Logger.log("Connection reconnect " + getService().getProtocolUid() + " in " + seconds); getService().getCoreService().connectionStateChanged(ConnectionState.CONNECTING, 1); } @Override public void reconnectionSuccessful() { Logger.log("Reconnected " + mMyOnlineInfo.getProtocolUid()); getService().getCoreService().connectionStateChanged(ConnectionState.CONNECTED, -1); } @Override public void reconnectionFailed(Exception e) { Logger.log("Reconnection failed " + mMyOnlineInfo.getProtocolUid()); connectionClosedOnError(e); } /** * @return the connection */ public XMPPConnection getConnection() { return connection; } /** * @return the onlineInfo */ public OnlineInfo getOnlineInfo() { return mMyOnlineInfo; } public long sendFile(FileMessage message) { List<File> files = new ArrayList<File>(message.getFiles().size()); for (FileInfo info : message.getFiles()) { files.add(new File(info.getFilename())); } return mFileTransferListener.sendFile(message.getContactUid() + "/" + message.getContactDetail(), files); } public void fileTransferResponse(FileMessage message, boolean accept) { mFileTransferListener.fileRespond(message, accept); } public void disconnect() { Executors.defaultThreadFactory().newThread(new Runnable() { @Override public void run() { connection.getChatManager().removeChatListener(mChatListener); connection.getRoster().removeRosterListener(mRosterListener); connection.removeConnectionListener(XMPPServiceInternal.this); connection.removePacketListener(mRosterListener); connection.disconnect(); mChatListener.onDisconnect(); mRosterListener.onDisconnect(); mFileTransferListener.onDisconnect(); getService().getCoreService().connectionStateChanged(ConnectionState.DISCONNECTED, 0); } }).start(); } public void getBuddyInfo(String jid, boolean shortInfo) { mRosterListener.getBuddyInfo(jid, shortInfo, false); } public void buddyAction(ItemAction action, Buddy buddy) { switch(action) { case ADDED: mRosterListener.addBuddy(buddy); break; case DELETED: if (buddy instanceof MultiChatRoom) { mChatListener.leaveChat(buddy.getProtocolUid()); getService().getCoreService().buddyAction(ItemAction.DELETED, buddy); } else { mRosterListener.removeBuddy(buddy); } break; case MODIFIED: RosterEntry e = connection.getRoster().getEntry(buddy.getProtocolUid()); if (TextUtils.isEmpty(e.getName()) || !e.getName().equals(buddy.getName())) { mRosterListener.renameBuddy(buddy); } if (e.getGroups().size() < 1 || (e.getGroups().size() > 0 && !e.getGroups().iterator().next().getName().equals(buddy.getGroupId()))) { mRosterListener.moveBuddy(buddy); } break; default: Logger.log("Unsupported buddy action: "+action, LoggerLevel.INFO); break; } } public void loadCard(String buddyId) { mRosterListener.loadCard(buddyId); } public void groupAction(ItemAction action, BuddyGroup group) { switch(action) { case ADDED: mRosterListener.addGroup(group); break; case DELETED: mRosterListener.removeGroup(group); break; case MODIFIED: mRosterListener.renameGroup(group); break; default: Logger.log("Unsupported group action: "+action, LoggerLevel.INFO); break; } } public void setStatus(byte statusId) { if (!connection.isConnected()) { return; } //TODO add full visibility list control support if (statusId == XMPPEntityAdapter.INVISIBLE_STATUS_ID) { Privacy privacy1 = new Privacy(); PrivacyItem item = new PrivacyItem(null, false, 1); item.setFilterPresence_out(true); privacy1.setPrivacyList("invisible", Arrays.asList(new PrivacyItem[]{ item })); connection.sendPacket(privacy1); Privacy privacy2 = new Privacy(); privacy2.setActiveName("invisible"); privacy2.setType(Type.SET); connection.sendPacket(privacy2); } Presence presence = getService().getEntityAdapter().userStatus2XMPPPresence(statusId); connection.sendPacket(presence); } public long sendMessage(TextMessage message) { try { return mChatListener.sendMessage(message); } catch (Exception e) { Logger.log(e); getService().getCoreService().notification(e.getLocalizedMessage()); return 0; } } public void cancelTransfer(long messageId) { mFileTransferListener.cancel(messageId); } public void sendTyping(String ownerUid) { mChatListener.sendTyping(ownerUid); } public void uploadIcon(byte[] bytes) { mRosterListener.uploadIcon(bytes); } public void addBuddy(String jid, String nickname) { Buddy buddy = new Buddy(jid, mMyOnlineInfo.getProtocolUid(), XMPPApiConstants.PROTOCOL_NAME, getService().getServiceId()); buddy.setName(nickname); buddy.setGroupId(ApiConstants.NO_GROUP_ID); mRosterListener.addBuddy(buddy); } public void authorizationResponse(String contactUid, boolean accept) { mRosterListener.authorizationResponse(contactUid, accept); } public void requestAvailableGroupchats() { mChatListener.requestAvailableGroupchats(); } public void leaveChat(String chatId) { mChatListener.leaveChat(chatId); } public void joinChat(String host, String chat, String nickname, String password, boolean createChat) { mChatListener.joinChat(host, chat, nickname, password, createChat); } public void getChatConfigurationForm(String protocolUid) { mChatListener.getChatConfigurationForm(protocolUid); } public void chatRoomConfiguration(String protocolUid, Map<String, String> values) { mChatListener.chatRoomConfiguration(protocolUid, values); } public void destroyChatRoom(String contactUid) { mChatListener.destroyChatRoom(contactUid); } /** * @return the service */ public XMPPCommonService getService() { return service; } public void serviceDiscoveryRequest() throws XMPPException { DiscoverItems discoItems = mServiceDiscoveryManager.discoverItems(connection.getServiceName()); Iterator<Item> it = discoItems.getItems(); while (it.hasNext()) { DiscoverItems.Item item = (DiscoverItems.Item) it.next(); Logger.log("Service available:" + item.getEntityID() + " " + item.getName()); } } /** * @return the mRosterListener */ public XMPPRosterListener getRosterListener() { return mRosterListener; } /** * @return the mChatListener */ public XMPPChatListener getChatListener() { return mChatListener; } /** * @return the mFileTransferListener */ public XMPPFileTransferListener getFileTransferListener() { return mFileTransferListener; } }